#include <iostream>

using namespace std;

int device;
int device_cnt = -1;

int check_host_ptr_attrib(void *ptr) {
	cudaPointerAttributes attributes;

	cudaError_t error = cudaPointerGetAttributes(&attributes, ptr);

	if (error != cudaSuccess) {
		printf("Failed to get pointer attributes: %s\n", cudaGetErrorString(error));
		return 1;
	}

	if (attributes.type == cudaMemoryTypeManaged) {
		printf("the cuda memory type is managed memory\n");
	} else if (attributes.type == cudaMemoryTypeDevice) {
		printf("the cuda memory type is device memory\n");
	} else if (attributes.type == cudaMemoryTypeHost) {
		printf("the cuda memory type is host memory\n");
	} else if (attributes.type == cudaMemoryTypeUnregistered) {
		printf("the cuda memory type is unregistered memory");
	} else {
		printf("unsupported memory type\n");
	}

	return 0;
}

int cuda_get_device_flags() {
	unsigned int flags;
	cudaError_t error = cudaGetDeviceFlags(&flags);

	if (error != cudaSuccess) {
		printf("Failed to get device flags: %s\n", cudaGetErrorString(error));
		return 1;
	}

	printf("device: %d, flags: %u\n", device, flags);

	if (flags & cudaDeviceMapHost) {
		printf("device: %d supports cudaHostAllocMapped\n", device);
	} else {
		printf("device: %d does not support cudaHostAllocMapped\n", device);
	}

	unsigned int flags_set = cudaDeviceScheduleAuto;
	unsigned int flags_new = 0;
	int ret;
	ret = cudaSetDeviceFlags(flags_set);
	ret += cudaGetDeviceFlags(&flags_new);

	if (ret != cudaSuccess) {
		printf("Failed to get device flags: %s\n", cudaGetErrorString(error));
		return 1;
	}

	printf("device: %d, new flags after set: %u\n", device, flags_new);

	if (flags_new & cudaDeviceMapHost) {
		printf("device: %d supports cudaHostAllocMapped\n", device);
	} else {
		printf("device: %d does not support cudaHostAllocMapped\n", device);
	}

	return 0;
}

int cuda_host_alloc_default() {
	int num = 100;
	size_t size = num * sizeof(int);

	// allocating memory on the host that can be accessed and shared by both the host and device
	int* h_data;
	cudaError_t status = cudaHostAlloc(&h_data, size, cudaHostAllocDefault);
	if (status != cudaSuccess) {
		std::cerr << "Failed to allocate host memory" << std::endl;
		return -1;
	}

	// initializing datas
	for (int i = 0; i < num; ++i) {
		h_data[i] = i;
	}

	// transfer data to device
	// some codes

	check_host_ptr_attrib(h_data);

	// free the memory
	cudaFreeHost(h_data);
	h_data = nullptr;

	return 0;
}

int main() {
	cudaGetDevice(&device);
	cudaGetDeviceCount(&device_cnt);

	cuda_get_device_flags();
	cuda_host_alloc_default();
	return 0;
}
